﻿using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;

namespace JuCheap.Core.Infrastructure.Extentions
{
    /// <summary>
    /// Queryable扩展
    /// </summary>
    public static class QueryableExtention
    {
        /// <summary>
        /// WhereIf[在condition为true的情况下应用Where表达式]
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="condition"></param>
        /// <param name="expression"></param>
        /// <returns></returns>
        public static IQueryable<T> WhereIf<T>(this IQueryable<T> source, bool condition, Expression<Func<T, bool>> expression)
        {
            return condition ? source.Where(expression) : source;
        }

        /// <summary>
        /// 分页扩展
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="query"></param>
        /// <returns></returns>
        public static async Task<PagedResult<T>> QueryPagesAsync<T>(this IQueryable<T> source, BaseCondition filter)
        {
            var count = await source.CountAsync();
            var rows = new List<T>();
            if (count > 0)
            {

                var pagedQuery = source.OrderBy(filter.SortField, filter.SortType)
                    .Skip((filter.PageIndex - 1) * filter.PageSize)
                    .Take(filter.PageSize);
                rows = await pagedQuery.ToListAsync();
            }

            return new PagedResult<T>
            {
                Datas = rows,
                Records = count,
                PageIndex = filter.PageIndex,
                PageSize = filter.PageSize
            };
        }

        /// <summary>
        /// 自定义排序
        /// </summary>
        public static IQueryable<T> OrderBy<T>(this IQueryable<T> query, string field, string orderby = "desc")
        {
            field = field.IsBlank() ? "CreateTime" : field;
            ParameterExpression p = Expression.Parameter(typeof(T));
            Expression key = Expression.Property(p, field);

            var propInfo = GetPropertyInfo(typeof(T), field);
            var expr = GetOrderExpression(typeof(T), propInfo);

            if (orderby.IsNotBlank() && orderby.Contains("desc"))
            {
                var method = typeof(Queryable).GetMethods().FirstOrDefault(m => m.Name == "OrderBy" && m.GetParameters().Length == 2);
                var genericMethod = method.MakeGenericMethod(typeof(T), propInfo.PropertyType);
                return (IQueryable<T>)genericMethod.Invoke(null, new object[] { query, expr });
            }
            else
            {
                var method = typeof(Queryable).GetMethods().FirstOrDefault(m => m.Name == "OrderByDescending" && m.GetParameters().Length == 2);
                var genericMethod = method.MakeGenericMethod(typeof(T), propInfo.PropertyType);
                return (IQueryable<T>)genericMethod.Invoke(null, new object[] { query, expr });
            }
        }
        /// <summary>
        /// 获取反射
        /// </summary>
        /// <param name="objType"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        private static PropertyInfo GetPropertyInfo(Type objType, string name)
        {
            var properties = objType.GetProperties();
            var matchedProperty = properties.FirstOrDefault(p => p.Name.IsEqual(name));
            if (matchedProperty == null)
                throw new ArgumentException("name");

            return matchedProperty;
        }
        /// <summary>
        /// 获取生成表达式
        /// </summary>
        /// <param name="objType"></param>
        /// <param name="pi"></param>
        /// <returns></returns>
        private static LambdaExpression GetOrderExpression(Type objType, PropertyInfo pi)
        {
            var paramExpr = Expression.Parameter(objType);
            var propAccess = Expression.PropertyOrField(paramExpr, pi.Name);
            var expr = Expression.Lambda(propAccess, paramExpr);
            return expr;
        }
    }
}
